const PENDING = "pending"
const FULFULLED = "fulfulled"
const REJECTED = "rejected"

class MyPromise {
  // 创建promise对象，需要传入一个函数，会立即执行
  constructor(handle) {
    try {
      handle(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  // promise的状态
  status = PENDING
  value = undefined
  reason = undefined
  successCallback = []
  failCallback = []

  // 调用resolve或reject方法，会永久改变promise的状态
  resolve = v => {
    if (this.status !== PENDING) return
    this.status = FULFULLED
    // 保存执行resolve时传入的参数
    this.value = v
    // 如果有成功回调，执行then中的第一个回调
    if (this.successCallback.length !== 0) {
      this.successCallback.forEach(cb => {
        cb()
      })
    }
  }
  reject = r => {
    if (this.status !== PENDING) return
    this.status = REJECTED
    // 保存执行reject时传入的参数
    this.reason = r
    if (this.failCallback.length !== 0) {
      this.failCallback.forEach(cb => {
        cb()
      })
    }
  }
  // 因为then方法可以实现链式调用，所以then方法的返回值是promise
  then(successCb, failCb) {
    // 如果then方法没有传参数，则设置默认函数
    successCb = successCb ? successCb : value => value
    failCb = failCb ? failCb : err => { throw err }
    let p1 = new MyPromise((resolve, reject) => {
      // 根据promise的状态执行相应的回调
      if (this.status === FULFULLED) {
        // 采用异步函数包裹代码，防止获取不到p1
        setTimeout(() => {
          try {
            // 将then方法中的返回值，作为下一个then方法的回调参数
            let x = successCb(this.value)
            resolvePromise(x, p1, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        // 采用异步函数包裹代码，防止获取不到p1
        setTimeout(() => {
          try {
            // 将then方法中的返回值，作为下一个then方法的回调参数
            let x = failCb(this.reason)
            resolvePromise(x, p1, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else {
        // 异步的情况
        // 用数组保存传入的两个回调（用数组的原因是可能执行多次then方法）
        successCb && this.successCallback.push(() => {
          setTimeout(() => {
            try {
              // 将then方法中的返回值，作为下一个then方法的回调参数
              let x = successCb(this.value)
              resolvePromise(x, p1, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
        failCb && this.failCallback.push(() => {
          setTimeout(() => {
            try {
              // 将then方法中的返回值，作为下一个then方法的回调参数
              let x = failCb(this.reason)
              resolvePromise(x, p1, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0);
        })
      }
    })
    return p1
  }
  catch(failCb) {
    return this.then(undefined, failCb)
  }
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(res => value)
    }, reason => {
      callback()
      return MyPromise.resolve(callback()).then(err => { throw err })
    })
  }

  // 静态方法，直接通过类名调用
  static resolve(val) {
    // 如果是传入的参数是promise对象，则直接返回这个promise;
    // 如果是非promise对象，则包装成promise对象
    if (val instanceof MyPromise) return val
    return new MyPromise(resolve => {
      resolve(val)
    })
  }
  static all(arr) {
    return new MyPromise((resolve, reject) => {
      let newArr = []
      // 用于统计新数组的元素
      let count = 0
      function update(key, value) {
        newArr[key] = value
        count++
        if (count === arr.length) resolve(newArr)
      }
      arr.forEach((item, i) => {
        MyPromise.resolve(item).then(value => {
          update(i, value)
        }, reason => {
          reject(reason)
        })
      })
    })
  }
}
function resolvePromise(x, p1, resolve, reject) {
  // 如果then方法的返回值是它本身，则直接抛出异常
  if (p1 === x) {
    return reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
  }

  if (x instanceof MyPromise) {
    // 如果是promise对象（通过then方法获取成功或者失败时的值）
    x.then(value => resolve(value), reason => reject(reason))
  } else {
    // 如果是普通值
    resolve(x)
  }
}

module.exports = MyPromise